﻿/*
 * Created by SharpDevelop.
 * User: e397496
 * Date: 17/04/2013
 * Time: 11:42
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Data.Odbc;
using BussisnesException;
using Entity;
using System.Data.OleDb;

namespace DAO
{
/// <summary>
/// Description of AbstractDAO.
/// </summary>
public abstract class AbstractDAO<T> where T : IEntity{
	
	protected enum OperationType{INSERT, UPDATE}
	
	//factory de conecciones
	private ConnectionFactory connectionFactory = ConnectionFactory.Instance;
	
/*	public AbstractDAO(ConnectionFactory connectionFactory){
		this.connectionFactory = connectionFactory;
	}*/
	
	/// <summary>
	/// Se busca una Entidad por el pk
	/// </summary>
	/// <param name="legajo">id</param>
	/// <returns>Objeto con los datos de la Entidad.</returns>
	public T findByPK(Object pk){
        T entity = default(T);
        try
        {
            Dictionary<String, Object> dic = new Dictionary<String,Object>{{ "@ID", pk }};
            // Se crea el comando con la sentencia Select.
            OleDbCommand command = this.createCommand("SELECT * FROM " + getTableName() + " WHERE " + getIdColumn() + "=@ID",
                                                      dic);
            entity = findObject(command);
        }
        catch (ErrorConnectionException e) {
            throw e;
        }
        catch (Exception ex)
        {
            // En caso de que se produzca un error, se lo lanza hacia la
            // capa superior.
            throw new ObjectNotFoundException(ex, pk);
        }
		return (T)entity;
	}

    protected void closeConnection(OleDbDataReader dr, OleDbCommand command)
    {
		// Se cierra el DataReader.
		if(dr!=null){
			dr.Close();
		}
		// Se marca al objeto para que sea liberado de la memoria.
		dr = null;
		if (command != null){
			// Se cierra la conexión.
            OleDbConnection connection = command.Connection;
			if (connection != null){
				// Se Slibera el recurso.
				connection.Close();
				connection.Dispose();
			}
			// Se libera el recurso.
			command.Dispose();
		}
	}

    protected T findObject(OleDbCommand command)
    {
		List<T> entitys = this.find(command);
		if(entitys != null && entitys.Count == 1){
			return entitys[0];
		}else{
			throw new ObjectNotFoundException();
		}
	}

    public bool exists(Object pk) {
        bool exist = false;
        try
        {
            exist = findByPK(pk) != null;
        }
        catch (ObjectNotFoundException e) {
            exist = false;
        }
        return exist;
    }
	
	/// <summary>
	/// Se buscan Entidades por distintos criterios.
	/// </summary
	/// <returns>Lista de objetos encontrados.</returns>*/
    protected List<T> find(OleDbCommand command)
    {
        // DataReader con registros de datos.
        OleDbDataReader dr = null;
        // Objeto con datos del User.
        List<T> entitys = new List<T>();
        try{
            // Se ejecuta el comando y devuelve un objeto del tipo DataReader
            // con los datos del User.
            dr = command.ExecuteReader();
            // Si se puede leer el objeto DataReader.
            T lastEntity = default(T);
            while (dr.Read()){
                // Se crea el objeto con los datos de la entidad.
                lastEntity = this.toEntity(dr);
                completeEntity(dr, lastEntity);
                while (dr.Read() && dr[this.getIdColumn()].Equals(lastEntity.getId()))
                {
                    completeEntity(dr, lastEntity);
                }
                entitys.Add(lastEntity);
            }
            if (entitys.Count.Equals(0)) {
                throw new ObjectNotFoundException();
            }
        } finally {
        	closeConnection(dr, command);
        }
        return entitys;
	}

    public void delete(Object pk){
        if (exists(pk))
        {
            OleDbCommand command = null;
            try
            {
                Dictionary<String, Object> parameters = new Dictionary<String, Object> { { "@PK", pk } };
                command = createCommand("delete from " + getTableName() + " where " + getIdColumn() + " = @PK",
                                        parameters);
                command.ExecuteNonQuery();
            }
            finally
            {
                closeConnection(null, command);
            }
        }
        else {
            throw new ObjectNotFoundException();
        }
	}
	
	public void update(T entity){
		excecuteOperation(OperationType.UPDATE, entity);
	}
	public virtual Object insert(T entity){
		return excecuteOperation(OperationType.INSERT, entity);
	}
	
	private Object excecuteOperation(OperationType operation, T entity){
        OleDbCommand command = null;
        Object pk = null;
		try{
            command = createOperationCommand(operation, entity);
            command.ExecuteNonQuery();
            command.CommandText = "SELECT @@IDENTITY";
		    pk = command.ExecuteScalar();
		}finally{
			closeConnection(null, command);
		}
        return pk;//TODO: Ver estoooo
	}

    protected OleDbCommand createCommand(String sql, Dictionary<String,Object> parameters)
    {
		// Se obtiene una conexión abierta.
        OleDbConnection connection = connectionFactory.getConnection();
		// Se crea el comando con la sentencia Select.
        OleDbCommand command = new OleDbCommand(sql, connection);
		
		foreach(KeyValuePair<String,Object> entry in parameters){
            //OleDbCommand parameter = command.Parameters.Add("?", types[i]);
            command.Parameters.AddWithValue(entry.Key, entry.Value);
			//parameter.Value = values[i];
		}
		return command;
	}

    protected abstract T toEntity(OleDbDataReader dr);
	protected abstract string getTableName();
	protected abstract string getIdColumn();
    protected abstract OleDbCommand createOperationCommand(OperationType type, T entidad);
    protected abstract void completeEntity(OleDbDataReader dr, T entity);
}
}